package com.nervenets.general.config;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.nervenets.general.config.support.CorsMappingResolver;
import com.nervenets.general.interceptor.WebInvokeTimeInterceptor;
import com.nervenets.general.jwt.AuthenticationInterceptor;
import com.nervenets.general.service.GlobalSecurityService;
import com.nervenets.general.service.MailService;
import com.nervenets.general.service.RedisService;
import com.nervenets.general.utils.JsonUtils;
import com.nervenets.general.utils.SpringContextHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.task.TaskExecutor;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.config.TaskManagementConfigUtils;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * Created by Joe on 2018/1/8.
 */
@Configuration
public class ApplicationConfiguration {
    @Resource
    private GlobalSecurityService globalSecurityService;
    @Resource
    private ApplicationProperties applicationProperties;
    @Resource
    private RedisService redisService;
    @Autowired
    private MailService mailService;
    @Autowired
    private Environment environment;

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                Map<String, CorsMappingResolver> resolvers = SpringContextHolder.getApplicationContext().getBeansOfType(CorsMappingResolver.class);
                if (resolvers.isEmpty()) {
                    registry.addMapping("/**")
                            .allowedOrigins(applicationProperties.getCors().getAllowedOrigins())
                            .allowedMethods(applicationProperties.getCors().getAllowedMethods())
                            .allowedHeaders(applicationProperties.getCors().getAllowedHeaders())
                            .allowCredentials(applicationProperties.getCors().isAllowCredentials())
                            .maxAge(3600);
                } else {
                    for (Map.Entry<String, CorsMappingResolver> entry : resolvers.entrySet()) {
                        entry.getValue().addCorsMappings(registry);
                    }
                }
            }

            @Override
            public void addInterceptors(InterceptorRegistry registry) {
                registry.addInterceptor(authenticationInterceptor())
                        .excludePathPatterns(applicationProperties.getAuthorizePermitAll())
                        .excludePathPatterns(applicationProperties.getSecretUrl().getUris());
                registry.addInterceptor(new WebInvokeTimeInterceptor());
            }
        };
    }

    @Bean
    public AuthenticationInterceptor authenticationInterceptor() {
        return new AuthenticationInterceptor(applicationProperties, globalSecurityService, redisService);
    }

    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);        // 设置核心线程数
        executor.setMaxPoolSize(200);        // 设置最大线程数
        executor.setQueueCapacity(100);      // 设置队列容量
        executor.setKeepAliveSeconds(120);   // 设置线程活跃时间（秒）
        executor.setThreadNamePrefix("user-rpt-");  // 设置默认线程名称
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());// 设置拒绝策略
        executor.setWaitForTasksToCompleteOnShutdown(true); // 等待所有任务结束后再关闭线程池
        return executor;
    }

    @Bean
    Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.featuresToDisable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
    }

    @Bean
    public MappingJackson2HttpMessageConverter longToStringConverter() {
        return JsonUtils.longToStringConverter();
    }

    @Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
    public AsyncAnnotationBeanPostProcessor asyncAnnotationBeanPostProcessor() {
        AsyncAnnotationBeanPostProcessor asyncAnnotationBeanPostProcessor = new AsyncAnnotationBeanPostProcessor();
        asyncAnnotationBeanPostProcessor.setExceptionHandler((ex, method, params) -> {
            ex.printStackTrace();
            String domain = applicationProperties.getBaseDomain();
            if (!domain.contains("localhost")) {
                if (redisService.canProceedCustom(ex.getClass().getSimpleName(), 60)) {
                    try {
                        mailService.exceptionEmailSend(environment.getProperty("spring.application.name"), "异步函数", JSON.toJSONString(params), ex);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        return asyncAnnotationBeanPostProcessor;
    }
}
